﻿using System.Globalization;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace IcicleFramework.Renderables.Components
{
    public class BasicAnimationComponent : BaseRenderableComponent, IAnimationComponent
    {
        protected enum AnimationState
        {
            Running,
            Paused,
            Stopped
        }

        #region Internal Variables
        
        /// <summary>
        /// The current starting frame of the animation.
        /// </summary>
        protected int fromFrame;

        /// <summary>
        /// The current ending frame of the animation.
        /// </summary>
        protected int toFrame;

        /// <summary>
        /// The current frame of animation.
        /// </summary>
        protected int currFrame;

        /// <summary>
        /// The number of seconds between frame changes.
        /// </summary>
        protected float frameSpeed;

        /// <summary>
        /// The current state of animation for this AnimatedImage.
        /// </summary>
        protected AnimationState state;

        /// <summary>
        /// The time since the frame was last changed.
        /// </summary>
        protected float lastChangeTime;

        /// <summary>
        /// The array of areas associated with each frame of animation from the source image.
        /// </summary>
        private Rectangle[] frames;
        
        #endregion


        #region Properties

        /// <summary>
        /// Gets or sets the starting frame of the animation cycle.
        /// </summary>
        public int StartingFrame
        {
            get { return fromFrame; }
            set { fromFrame = value < 0 ? 0 : value; }
        }

        /// <summary>
        /// Gets or sets the ending frame of the animation cycle.
        /// </summary>
        public int EndingFrame
        {
            get { return toFrame; }
            set { toFrame = value > TotalFrames - 1 ? TotalFrames - 1 : value; }
        }

        /// <summary>
        /// Gets the total number of frames for this AnimatedImage.
        /// </summary>
        public int TotalFrames
        {
            get { return frames.Length; }
        }

        /// <summary>
        /// Gets or sets the speed at which animation frames are swapped.
        /// </summary>
        public float FrameSpeed
        {
            get { return frameSpeed; }
            set
            {
                if (value <= 0.0f)
                    value = 0.01f;

                frameSpeed = value;
            }
        }
        
        public int NumRows { get; set; }

        public int NumCols { get; set; }

        //public new ITextured Parent { get; set; }

        #endregion


        #region Constructors

        public BasicAnimationComponent()
        {
            
        }

        public BasicAnimationComponent(ITextured parentImage, int numRows, int numCols)
        {
            this.Parent = parentImage;
            lastChangeTime = 0f;
            frameSpeed = 100f;
            state = AnimationState.Stopped;

            NumRows = numRows;
            NumCols = numCols;
        }

        protected BasicAnimationComponent(BasicAnimationComponent old)
        {
            this.Parent = Parent;

            this.lastChangeTime = 0f;
            this.frameSpeed = old.frameSpeed;
            this.state = AnimationState.Running;

            this.currFrame = old.currFrame;
            this.fromFrame = old.fromFrame;

            frames = new Rectangle[old.frames.Length];

            for (int i = 0; i < frames.Length; i++)
                frames[i] = new Rectangle(old.frames[i].X, old.frames[i].Y, old.frames[i].Width, old.frames[i].Height);
        }

        #endregion


        public override void Initialize()
        {
            BuildFrames(((ITextured)Parent).Texture, NumRows, NumCols);
            currFrame = fromFrame;

            ((ITextured)Parent).Source = frames[currFrame];

            Parent.Origin = new Vector2(frames[currFrame].Width / 2f, frames[currFrame].Height / 2f);

            state = AnimationState.Running;
        }


        public void StartAnimation()
        {
            state = AnimationState.Running;
        }

        public void StopAnimation()
        {
            state = AnimationState.Stopped;
            currFrame = fromFrame;
        }

        public void PauseAnimation()
        {
            state = AnimationState.Paused;
        }

        /// <summary>
        /// A BasicAnimationComponent has nothing to draw but we need to include this...
        /// </summary>
        public override void Draw(SpriteBatch spriteBatch, Vector2 position)
        { }

        public override void Update(GameTime gameTime)
        {
            lastChangeTime += (float)gameTime.ElapsedGameTime.TotalSeconds;

            //Change the frame if it's time and we're not paused or stopped.
            if (state == AnimationState.Running && lastChangeTime >= frameSpeed)
            {
                currFrame = currFrame == toFrame ? fromFrame : currFrame + 1;
                lastChangeTime = 0f;

                ((ITextured)Parent).Source = frames[currFrame];
            }
        }

        
        #region Build Frames

        /// <summary>
        /// Builds up the list of sources for each frame using the given texture.
        /// </summary>
        /// <param name="texture">The texture containing each frame for the AnimatedImage.</param>
        /// <param name="numRows">The number of rows of frames in the texture.</param>
        /// <param name="numCols">The number of columns of frames in the texture.</param>
        private void BuildFrames(Texture2D texture, int numRows, int numCols)
        {
            //We know how many Rectangles/Frames we need by the number of rows and columns
            int numFrames = numRows * numCols;
            frames = new Rectangle[numFrames];

            int frame = 0;
            int width = (texture.Width / numCols);
            int height = (texture.Height / numRows);

            //Build up each frame by row then column
            for (int row = 0; row < numRows; row++)
            {
                int yStart = row * height;

                for (int col = 0; col < numCols; col++)
                {
                    int xStart = col * width;
                    frames[frame++] = new Rectangle(xStart, yStart, width, height);
                }
            }

            fromFrame = 0;
            toFrame = frame - 1;
        }

        #endregion

        public override IRenderableComponent DeepClone()
        {
            return new BasicAnimationComponent(this);
        }

        public override void Deserialize(XElement element)
        {
            int numRows = 1, numCols = 1;
            float framesPerSecond = 1.0f;

            if (element.Element("numRows") != null)
                numRows = int.Parse(element.Element("numRows").Value, NumberStyles.Integer, CultureInfo.InvariantCulture);

            if (element.Element("numCols") != null)
                numCols = int.Parse(element.Element("numCols").Value, NumberStyles.Integer, CultureInfo.InvariantCulture);

            if (element.Element("fps") != null)
            framesPerSecond = float.Parse(element.Element("fps").Value, NumberStyles.Float, CultureInfo.InvariantCulture);

            this.NumRows = numRows;
            this.NumCols = numCols;
            this.frameSpeed = framesPerSecond;
        }
    }
}
